#include "CloudDataPubSubTypes.h"
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <fastdds/dds/publisher/DataWriterListener.hpp>
#include "util/file.h"

using namespace eprosima::fastdds::dds;
using namespace std;
using namespace base::utils;

class CloudDataPublisher
{
private:
    CloudData cloud_data_;

    DomainParticipant *participant_;

    Publisher *publisher_;

    Topic *topic_;

    DataWriter *writer_;

    TypeSupport type_;

    string data_;

    int index;

    class PubListener: public DataWriterListener
    {
    public:
        PubListener()
            : matched_(0)
        {}

        ~PubListener() override
        {}

        void on_publication_matched(DataWriter *writer,
                                    const PublicationMatchedStatus &info) override
        {
            if (info.current_count_change == 1)
            {
                matched_ = info.total_count;
                std::cout << "Publisher matched." << std::endl;
            }
            else if (info.current_count_change == -1)
            {
                matched_ = info.total_count;
                std::cout << "Publisher unmatched." << std::endl;
            }
            else
            {
                std::cout << info.current_count_change
                          << " is not a valid value for PublicationMatchedStatus current count change."
                          << std::endl;
            }
        }

        std::atomic_int matched_;
    } listener_;

public:
    CloudDataPublisher()
        : participant_(nullptr), publisher_(nullptr), topic_(nullptr), writer_(nullptr), type_(new CloudDataPubSubType()), index(0)
    {
    }

    virtual ~CloudDataPublisher()
    {
        if (writer_ != nullptr)
        {
            publisher_->delete_datawriter(writer_);
        }
        if (publisher_ != nullptr)
        {
            participant_->delete_publisher(publisher_);
        }
        if (topic_ != nullptr)
        {
            participant_->delete_topic(topic_);
        }
        DomainParticipantFactory::get_instance()->delete_participant(participant_);
    }

    // Initialize the publisher
    bool Init()
    {
        cloud_data_.height(480);
        cloud_data_.width(640);
        // for (int index = 0; index < 11000000; index++)
        for (int index = 0; index < 101; index++)
        {
            cloud_data_.data().push_back(10);
        }

        DomainParticipantQos participantQos;
        participantQos.name("Participant_publisher");
        participant_ = DomainParticipantFactory::get_instance()->create_participant(0, participantQos);
        if (participant_ == nullptr)
        {
            return false;
        }

        // Register the type
        type_.register_type(participant_);

        // Create the publications Topic
        topic_ = participant_->create_topic("CloudDataTopic", "CloudData", TOPIC_QOS_DEFAULT);
        if (topic_ == nullptr)
        {
            return false;
        }

        // Create the Publisher
        publisher_ = participant_->create_publisher(PUBLISHER_QOS_DEFAULT, nullptr);
        if (publisher_ == nullptr)
        {
            return false;
        }

        // Create the DataWriter
        writer_ = publisher_->create_datawriter(topic_, DATAWRITER_QOS_DEFAULT, &listener_);
        if (writer_ == nullptr)
        {
            return false;
        }
        return true;
    }

    bool Publish()
    {
        if (listener_.matched_ > 0)
        {
            bool res = writer_->write(&cloud_data_);
            if (res)
            {
                cout << "publish data size: " << cloud_data_.data().size() << endl;
            }
            return res;
        }
        return false;
    }

    void Run(uint32_t samples)
    {
        uint32_t samples_sent = 0;
        while (samples_sent < samples)
        {
            if (Publish())
            {
                samples_sent++;
                std::cout << "Message index: " << cloud_data_.height() << std::endl;
            }
            else
            {
                cout << "publish fail" << endl;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
    }
}; // class CloudDataPublisher

int main(int argc, char **argv)
{
    std::cout << "Starting publisher." << std::endl;
    int samples = 100;

    CloudDataPublisher *mypub = new CloudDataPublisher();
    if (mypub->Init())
    {
        mypub->Run(static_cast<uint32_t>(samples));
    }
    else
    {
        cout << "init fail" << endl;
    }
    delete mypub;
    return 0;
}
